<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>

<title>CodeIgniter Project Documentation : MPTtree</title>

<style type='text/css' media='all'>@import url('./userguide.css');</style>
<style type='text/css' media='all'>@import url('./images/userguide.css');</style>
<link rel='stylesheet' type='text/css' media='all' href='../userguide.css' />

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv='expires' content='-1' />
<meta http-equiv= 'pragma' content='no-cache' />
<meta name='robots' content='all' />

</head>
<body>

<!-- START NAVIGATION -->
<div id="nav"><div id="nav_inner"></div></div>
<div id="nav2"><a name="top">&nbsp;</a></div>
<div id="masthead">
<table cellpadding="0" cellspacing="0" border="0" style="width:100%">
<tr>
<td><h1>MPTtree User Guide 0.1.6</h1></td>
<td id="breadcrumb_right"><a href="../toc.html">Table of Contents Page</a></td>
</tr>
</table>
</div>
<!-- END NAVIGATION -->


<!-- START BREADCRUMB -->
<table cellpadding="0" cellspacing="0" border="0" style="width:100%">
<tr>
<td id="breadcrumb">
<a href="http://codeigniter.com/">CodeIgniter Home</a> &nbsp;&#8250;&nbsp;
<a href="../index.html">User Guide Home</a> &nbsp;&#8250;&nbsp;
MPTtree
</td>
<td id="searchbox"><form method="get" action="http://www.google.com/search">
<input type="hidden" name="as_sitesearch" id="as_sitesearch" value="#" />Search Project User Guide&nbsp;
<input type="text" class="input" style="width:200px;" name="q" id="q" size="31" maxlength="255" value="" />&nbsp;
<input type="submit" class="submit" name="sa" value="Go" /></form></td>
</tr>
</table>
<!-- END BREADCRUMB -->

<br clear="all" />


<!-- START CONTENT -->
<div id="content">

<h1>MPTtree Model</h1>

<p>MPTtree is a CodeIgniter model to handle Modified Preorder Tree Traversal, or Nested Sets Trees as it also is called.</p>

<ul>
<li><a href="#load">Load the Model</a></li>
<li><a href="#get">Get Node</a></li>
<li><a href="#active">Active Record helpers</a></li>
<li><a href="#update">Update Node</a></li>
<li><a href="#insert">Insert Node</a></li>
<li><a href="#move">Move Node</a></li>
<li><a href="#delete">Delete Nodes</a></li>
<li><a href="#validate">Validate Tree/Node</a></li>
<li><a href="#ORM">Object-Relational Mapping (ORM)</a></li>
<li><a href="#iterators">Iterators</a></li>
<li><a href="#change">Change Log</a></li>
</ul>

<h2>Short About Modified Preorder Tree Traversal</h2>

<p>The Normal "easy" type of trees uses a column called parent_id which contains the parent node's id. This is easy at a glance
because adding and remving leaves is very easy.</p>

<p>But it also has some problems:</p>

<ul>
<li>When someone deletes a node which has subnodes, those connections must be re-established to another node.</li>
<li>It takes many querys to get all descendants or parents for a given node, in Nested Sets it takes only one.</li>
<li>You have two columns with the same data (id and parent_id).</li>
</ul>

<p>A Nested Sets Tree is organized with two values: lft (left) and rgt (right). Those determine which node is parent to another.</p>

<img src="images/sitepoint_numbering.gif" />

<p>The lower nodes all have <var>lft</var> and <var>rgt</var> values which lies within the range
of the parents <var>lft</var> and <var>rgt</var> values.<br />
So we can see that the Red node lies within the Fruits node by looking at the lft values:</p>

<p>Fruit's lft &lt; Red's lft &lt; children to Red &lt; Red's rgt &lt; Fruit's rgt</p>

<p>A disadvantage with Nested Sets is that it often requires multiple querys when inserting,
moving or deleteing items in the tree, but it requires less querys when fetching data.</p>

<a name="load">&nbsp;</a>
<h1>Load The Model</h1>

<h2>Recommended table structure</h2>

<p>This is the recommended table structure (more columns can be inserted, MPTtree can handle any number of extra columns):</p>

<table cellpadding="0" cellspacing="1" border="0" style="width:50%" class="tableborder">
<tr>
	<th>Column name (default)</th>
	<th>Type</th>
	<th>Attributes</th>
	<th>Extra</th>
</tr>
<tr>
	<td class="td">id</td>
	<td class="td">int</td>
	<td class="td">unsigned</td>
	<td class="td">auto_increment</td>
</tr>
<tr>
	<td class="td">lft</td>
	<td class="td">int</td>
	<td class="td">unsigned</td>
	<td class="td"> </td>
</tr>
<tr>
	<td class="td">rgt</td>
	<td class="td">int</td>
	<td class="td">unsigned</td>
	<td class="td"> </td>
</tr>
<tr>
	<td class="td">title</td>
	<td class="td">varchar</td>
	<td class="td"> </td>
	<td class="td"> </td>
</tr>
<tr>
	<td class="td">...</td>
	<td class="td">...</td>
	<td class="td">...</td>
	<td class="td">...</td>
</table>
<h2>Traditional loading</h2>

<p>To load the model use the following code:</p>

<code>
	$this-&gt;load-&gt;model('<var>MPTtree</var>');<br />
	$this-&gt;MPTtree-&gt;set_table('<var>Tablename</var>');
</code>

<p>You load the model as usual, but then you have to set the parameters (ie. tablename).</p>

<p class="important"><strong>Important:</strong>&nbsp;
You must use the <dfn>set_table()</dfn> or <dfn>set_opts()</dfn> to initialize settings before you use the model.</p>

<code>// Or instead of set_table()...<br />
$this-&gt;MPTtree-&gt;set_opts(array(
'table' =&gt; '<var>Tablename</var>',<br />
'left' =&gt; '<var>Left column name</var>',<br />
'right' =&gt; '<var>Right column name</var>',<br />
'id' =&gt; '<var>Id column name</var>',<br />
'title' =&gt; '<var>Title column name</var>'));
</code>

<p>The default values for those parameters are:</p>

<table cellpadding="0" cellspacing="1" border="0" style="width:50%" class="tableborder">
<tr>
	<th>Parameter</th>
	<th>Key</th>
	<th>Default value</th>
</tr>
<tr>
	<td class="td">Tablename</td>
	<td class="td">table</td>
	<td class="td">NULL</td>
</tr>
<tr>
	<td class="td">Left column name</td>
	<td class="td">left</td>
	<td class="td">lft</td>
</tr>
<tr>
	<td class="td">Right column name</td>
	<td class="td">right</td>
	<td class="td">rgt</td>
</tr>
<tr>
	<td class="td">Id column name</td>
	<td class="td">id</td>
	<td class="td">id</td>
</tr>
<tr>
	<td class="td">Title column name</td>
	<td class="td">title</td>
	<td class="td">title</td>
</tr>
</table>

<p>The <kbd>title</kbd> column is optional, you don't need it as long as you don't use the
<dfn>display()</dfn> method, described in the section <a href="#validate">Validate Tree/Node</a> or the <dfn>xpath()</dfn> method,
described in the section <a href="#get">Get Node</a>.</p>

<p>The <kbd>id</kbd> column is preferably of the type unique unsigned int with auto increment.<br />
MPTtree will not set this column, only use it as a unique identifier.</p>

<p><strong>Note:</strong> Only the tablename is required to specify
(so you can skip the use of <dfn>set_opts()</dfn>).</p>

<h2>Loading with the custom loader</h2>

<p>To install the custom loader, just drop <kbd>MY_Loader.php</kbd> in your <kbd>application/libraries</kbd> folder.
If you already have a custom <kbd>MY_Loader.php</kbd>, copy the <dfn>MPTT()</dfn> function from the MPTtree custom loader to yours.</p>

<p>Usage:</p>

<code>
$this-&gt;load-&gt;MPTT('<var>tablename</var>' [, '<var>name</var>'] [, <var>options_array</var>]);
</code>

<p>The <dfn>MPTT()</dfn> method will create an instance of MPTtree with the name <var>name</var> (if omitted, <var>tablename</var> will be used),
and options <var>options_array</var> (sent to set_opts).<br />
So if you specify pages as tablename, then <dfn>MPTT()</dfn> will put a MPTtree instance at $this-&gt;pages , with the table pages.<br />
If you specify another name, the MPTtree instance will be saved under that name.</p>

<p>Example:</p>

<code>
// loading MPTtree to $this-&gt;pages<br />
$this-&gt;load-&gt;MPTT('page_tree', 'pages', array('title' =&gt; 'uriseg'));<br />
// fetching root node<br />
$root = $this-&gt;pages-&gt;get_root();
</code>

<a name="get">&nbsp;</a>
<h1>Get Node</h1>

<p>To fetch a node, you have to know at which <kbd>lft</kbd> (or whatever you call your left column) the node resides in.
You usually get this information from data which you already have fetched from the database,
a good start is the <dfn>get_root()</dfn> method:</p>

<h2>get_root()</h2>

<p>This method returns the table row containing the rootnode's data.<br />
An example:</p>

<code>
$root = $this-&gt;MPTtree-&gt;get_root();<br />
print_r($root);<br />
<br />
// Prints for example the following (depending on content in the database):<br />
Array
(
    [id] =&gt; 34,
    [lft] =&gt; 1,
    [rgt] =&gt; 8,
    [title] =&gt; Root Page,
    [content] =&gt; FOO BAR...,
)
</code>

<p>From there we can easily continue by using the values we have by using <dfn>get_children()</dfn>:</p>

<h2>get_children(<var>lft</var>,<var>rgt</var>)</h2>

<p>Returns all first level children to the node with the lft <var>lft</var> and rgt <var>rgt</var>.</p>

<code>
$children = $this-&gt;MPTtree-&gt;get_children($root['lft'],$root['rgt']);<br />
foreach($children as $child){<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $child['title'] . "\n";<br />
}<br />
<br />
// May output:<br />
Parent 1<br />
A child to root<br />
...<br />
...
</code>

<p>But this only returns the first level children,
to get all childrens (including grandchildren and grand grandchildren and so on) we use <dfn>get_descendants()</dfn>:</p>

<h2>get_descendants(<var>lft</var>,<var>rgt</var>[,<var>with_level_col</var> = <kbd>false</kbd>])</h2>

<p>Returns all children to the node with the lft <var>lft</var> and rgt <var>rgt</var>. If <var>with_level_col</var> is set to true, a new column called level will be added to the result (containing the relative level where the nodes are found at  (child nodes have 1 as level)).</p>

<code>
$children = $this-&gt;MPTtree-&gt;get_descendants($root['lft'],$root['rgt']);<br />
foreach($children as $child){<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $child['title'] . "\n";<br />
}<br />
<br />
// May output:<br />
Parent 1<br />
A child<br />
A child to root<br />
...<br />
...
</code>

<h2>has_children(<var>lft</var>, <var>rgt</var>)</h2>

<p>Returns true if the node with the specified lft and rgt values has children.</p>

<h2>get_node(<var>lft</var>)</h2>

<p>Returns the row of the node with the lft <var>lft</var> (returns false if none are found).</p>

<p>If we know a lft address, we can use it directly
(otherwise use a lft you have obtained from children or parents):</p>

<code>
$node = $this-&gt;MPTtree-&gt;get_node(3);<br />
echo $node['title'];<br />
// result: A child
</code>

<h2>get_node_byid(<var>id</var>)</h2>

<p>Works like <dfn>get_node()</dfn>, but with the exception that you specify the <kbd>id</kbd> instead of the <kbd>lft</kbd> value</p>

<h2>get_parents(<var>lft</var>,<var>rgt</var>)</h2>

<p>If we want to know which nodes are parents to a given node, we can use <dfn>get_parents()</dfn>:</p>

<code>
$parents = $this-&gt;MPTtree-&gt;get_parents(3,4);<br />
// The node from the previous example<br />
<br />
foreach($parents as $parent){<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $parent['title']; // Would produce "Parent 1" and "Root Page" (the closest related first)<br />
}
</code>

<h2>get_parent(<var>lft</var>,<var>rgt</var>)</h2>

<p>Returns the closest related parent to the node with the lft <var>lft</var> and rgt <var>rgt</var>.</p>

<h2>get_decendants_where(<var>lft</var>,<var>rgt</var>,<var>where</var>)</h2>

<p>Returns all descendants to a given node where the <var>where</var> clause matches.</p>

<p><strong>Note:</strong>&nbsp; The <var>where</var> parameter is sent to the <dfn>where()</dfn>
method in CodeIgniter's Active Record class.</p>

<h2>get_children_where(<var>lft</var>,<var>rgt</var>,<var>where string</var>)</h2>

<p>Returns all children to a given node where the <var>where</var> clause matches.</p>

<p>The <var>where</var> is the part of the query after the <kbd>WHERE</kbd> part, and will do the final filtering</p>

<code>
$children = $this-&gt;MPTtree-&gt;get_children_where(1,8,array('title' =&gt; 'Parent 1');<br />
// Returns the Parent 1 node (lft = 2 and title = Parent 1)
</code>

<p><strong>Note:</strong>&nbsp; The <var>where</var> parameter is sent to the <dfn>where()</dfn>
method in CodeIgniter's Active Record class.</p>

<h2>xpath('<var>path</var>' [,'<var>separator</var>'] [,<var>root lft</var>])</h2>

<p><a name="get_xpath">&nbsp;</a>The <dfn>xpath()</dfn> method is an easy way to fetch a node in the tree if you know where it lies.</p>

<p>The <var>path</var> parameter is the path to the node ('/child/grandchild'...), where the segments in the path
are the nodes' respective values in the <kbd>title column</kbd>.</p>

<p>The optional <var>separator</var> parameter is the separator used in the path, which can be anything
(but preferably something which cannot be found in the <kbd>title column</kbd>).<br />
The default value is a slash ('/').</p>

<p>The second optional parameter, <var>root lft</var>, is the lft of the node to start from,
default is the rootnode (where lft = 1).</p>

<code>
print_r($this-&gt;MPTtree-&gt;xpath(<var>'/Parent 1/A child'</var>));<br />
// will output:
Array
(
    [id] =&gt; 36,
    [lft] =&gt; 3,
    [rgt] =&gt; 4,
    [title] =&gt; A child
)<br />
<br />
print_r($this-&gt;MPTtree-&gt;xpath(<var>'&amp;A child'</var>, <var>'&amp;'</var>, <var>2</var>));<br />
// will also output:
Array
(
    [id] =&gt; 36,
    [lft] =&gt; 3,
    [rgt] =&gt; 4,
    [title] =&gt; A child
)<br />
// because the node Parent 1 has the lft 2, and we are starting the path from there
</code>

<p>If no node was found at the supplied path, <dfn>xpath()</dfn> returns <kbd>false</kbd>.</p>

<p><strong>Note:</strong>&nbsp; The path does not need to start with a separator, it is just there
for clarity, and it does not matter if the path ends with a separator either.</p>

<h2>xpath2('<var>path</var>' [,'<var>separator</var>'] [,<var>root lft</var>])</h2>

<p>This is the old variant of the <dfn>xpath()</dfn> method.
It is in most cases much slower than the one above, but the new <dfn>xpath()</dfn>
has one drawback: it can find nodes with similar paths.</p>

<p>If you have four paths like this:</p>
<ul>
<li>/fruits/green/apple</li>
<li>/green/apple</li>
<li>/apple/laptops</li>
<li>/apple/computers/laptops</li>
</ul>

<p>The new <dfn>xpath()</dfn> method will find both /fruits/green/apple and /green/apple
if you call xpath('/green/apple'). To correct this, the new variant calls this method, <dfn>xpath2()</dfn>.
This makes <dfn>xpath()</dfn> a lot slower in certain situations, like those above.</p>

<p>The same will occur if you call xpath('/apple/laptops'), it will find /apple/laptops
and /apple/computers/laptops.</p>

<p>This variant returns the same data and is used in the same way as <dfn>xpath()</dfn>.
In most cases this method is not needed, but if you have paths like those above,
this old version may be a better choice than the new one (in case of performance).</p>

<h2>tree2array([<var>root</var> = 1])</h2>

<p>This method fetches the whole tree and translates it to a PHP array. The <var>root</var> value is optional, it determines the root of operation.</p>

<p>Example:</p>

<code>
$tree = $this->MPTtree->tree2array();<br />
print_r($tree); // will produce:<br />
Array([0]&nbsp;=>&nbsp;Array([id]&nbsp;=>&nbsp;34,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lft]&nbsp;=>&nbsp;1,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[rgt]&nbsp;=>&nbsp;8,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[title]&nbsp;=>&nbsp;'Root&nbsp;Page',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[children]&nbsp;=>&nbsp;Array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[0]&nbsp;=>&nbsp;Array([id]&nbsp;=>&nbsp;445,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lft]&nbsp;=>&nbsp;2,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[rgt]&nbsp;=>&nbsp;5<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[title]&nbsp;=>&nbsp;'Parent&nbsp;1',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[children]&nbsp;=>&nbsp;Array(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[0]&nbsp;=>&nbsp;Array([id]&nbsp;=>&nbsp;46,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lft]&nbsp;=>&nbsp;3,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[rgt]&nbsp;=>&nbsp;4,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[title]&nbsp;=>&nbsp;'A&nbsp;child'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[1]&nbsp;=>&nbsp;Array([id]&nbsp;=>&nbsp;35,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[lft]&nbsp;=>&nbsp;6,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[rgt]&nbsp;=>&nbsp;7,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[title]&nbsp;=>&nbsp;'A&nbsp;child&nbsp;to&nbsp;root'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
)
</code>

<h2>Example Table</h2>

<p>For these examples we have used a table which for example may look like this:</p>

<table cellpadding="0" cellspacing="1" border="0" style="width:80%" class="tableborder">
<tr>
	<th>id</th>
	<th>lft</th>
	<th>rgt</th>
	<th>title</th>
	<th>More columns here...</th>
</tr>
<tr>
	<td class="td">34</td>
	<td class="td">1</td>
	<td class="td">8</td>
	<td class="td">Root Page</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">445</td>
	<td class="td">2</td>
	<td class="td">5</td>
	<td class="td">Parent 1</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">36</td>
	<td class="td">3</td>
	<td class="td">4</td>
	<td class="td">A child</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">35</td>
	<td class="td">6</td>
	<td class="td">7</td>
	<td class="td">A child to root</td>
	<td class="td"></td>
</tr>
</table>

<p> <br /></p>

<a name="active">&nbsp;</a>
<h1>Active Record helpers</h1>

<p>I have included some Active Record helpers which enables the user to fetch data from the tree
in a more customized manner. These functions are used as the <dfn>from()</dfn> method of the
Active Record class, they construct a subquery and put it in the <kbd>FROM</kbd> part of the AR query.</p>

<p class="important"><strong>Important:</strong>&nbsp;
Only one call per query to the following methods are allowed (otherwise you will end up with
multiple subqueries with the same aliases). You can use methods which utilizes
different aliases in the same query, though.</p>

<h2>AR_from_descendants_of(<var>lft</var>, <var>rgt</var>)</h2>

<p>This method adds a subquery which fetches all descendants to the node with lft <var>lft</var>
and rgt <var>rgt</var>.</p>

<p>Example:</p>

<code>
$this-&gt;db-&gt;select('name, address');<br />
$this-&gt;MPTtree-&gt;AR_from_descendants_of(3,10);<br />
$this-&gt;db-&gt;order_by('name','asc');<br />
print_r($this-&gt;db-&gt;get());<br />
</code>

<p><strong>Note:&nbsp;</strong> The subquery's alias is <kbd>descendant</kbd>, but it is not needed to use unless you have
multiple tables (and if you have, all of them must have an alias).</p>

<h2>AR_from_children_of(<var>lft</var>, <var>rgt</var>)</h2>

<p>This method adds a subquery which fetches all children to the node with lft <var>lft</var>
and rgt <var>rgt</var>.</p>

<code>
$this-&gt;db-&gt;select('child.title, customer.name');<br />
$this-&gt;MPTtree-&gt;AR_from_children_of(3,10);<br />
$this-&gt;db-&gt;from('customers_table AS customer'); // multiple froms and such are supported<br />
$this-&gt;db-&gt;where('child.cutomer_id = customer.id');<br />
$this-&gt;db-&gt;order_by('child.title');<br />
print_r($this-&gt;db-&gt;get());<br />
</code>

<p><strong>Note:&nbsp;</strong> The alias of this subquery is <kbd>child</kbd> (as above you can often skip this).</p>

<h2>AR_from_parents_of(<var>lft</var>, <var>rgt</var>)</h2>

<p>This method adds a subquery which fetches all parents to the node with with lft <var>lft</var>
and rgt <var>rgt</var>. It does <strong>not</strong> fetch the node itself, only the parents.</p>

<p>It works the same as those above.</p>

<p><strong>Note:&nbsp;</strong> The alias of this subquery is <kbd>parent</kbd>.</p>

<a name="update">&nbsp;</a>
<h1>Update Node(<var>lft</var>, <var>data array</var>)</h1>

<p>To update a node simply use the <dfn>update_node()</dfn> function:</p>

<code>
$this-&gt;MPTtree-&gt;update_node(1,array('title' =&gt; 'The new Root Page!', 'content' =&gt; 'I am a string!'));<br />
// Will set the title of the root node (with lft=1) to 'The new Root Page!' and the content field to 'I am a string!'
</code>

<p>It uses CodeIgniter's Active Record class to construct and execute the update query,
the <var>data array</var> is sent as the second parameter to the <dfn>update()</dfn> method.</p>

<a name="insert">&nbsp;</a>
<h1>Insert Node</h1>

<p>To insert a node you can use one of the following methods:</p>

<ul>
<li>
<b>insert_root(<var>data array</var>)</b>
<p>adds a root node</p>
<p class="important"><strong>Important:</strong>&nbsp; The tree can only contain <strong>one</strong> root node,
If you need more, put those as children to a rootnode with no data.</p></li>
<li>
<b>insert_node_before(<var>lft</var>, <var>data array</var>)</b>
<p>inserts the new node before the node with the lft specified</p> </li>
<li>
<b>insert_node_after(<var>lft</var>, <var>data array</var>)</b>
<p>inserts the new node after the node with the lft specified</p> </li>
<li>
<b>append_node(<var>lft</var>, <var>data array</var>)</b>
<p>inserts the node as the first child to the node with the lft specified</p> </li>
<li>
<b>append_node_last(<var>lft</var>, <var>data array</var>)</b>
<p>inserts the node as the last child to the node with the lft specified</p> </li>
<li>
<b>insert_node(<var>target lft</var>, <var>data array</var>)</b>
<p>raw access, uses the target lft directly <b>!Discouraged!</b></p> </li>
</ul>

<p>All these are quite straightforward, all they need is a lft value and the data array
(except for <dfn>insert_root()</dfn> which only requires the data array).<br />
If the operation was successful, the return value is an array with the new <kbd>lft</kbd>, <kbd>rgt</kbd> and <kbd>id</kbd> (in that order)
value of the inserted node<br />
(with the exception of <dfn>insert_root()</dfn>, which returns <kbd>true</kbd> on success), otherwise false is returned.</p>
<p>For example:</p>

<code>
$this-&gt;MPTtree-&gt;append_node(3,array('title' =&gt; 'A new child!'));
</code>

<p>If this is used on the table used above, the result will be:</p>
<table cellpadding="0" cellspacing="1" border="0" style="width:80%" class="tableborder">
<tr>
	<th>id</th>
	<th>lft</th>
	<th>rgt</th>
	<th>title</th>
	<th>More columns here...</th>
</tr>
<tr>
	<td class="td">34</td>
	<td class="td">1</td>
	<td class="td">10</td>
	<td class="td">Root Page</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">445</td>
	<td class="td">2</td>
	<td class="td">7</td>
	<td class="td">Parent 1</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">36</td>
	<td class="td">3</td>
	<td class="td">6</td>
	<td class="td">A child</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">446</td>
	<td class="td">4</td>
	<td class="td">5</td>
	<td class="td">A new child!</td>
	<td class="td"></td>
</tr>
<tr>
	<td class="td">35</td>
	<td class="td">8</td>
	<td class="td">9</td>
	<td class="td">A child to root</td>
	<td class="td"></td>
</tr>
</table>

<p>They all use CodeIgniter's Active Record class to construct and execute the insert queries,
the <var>data array</var> is sent as the second parameter to the <dfn>insert()</dfn> method.</p>


<a name="move">&nbsp;</a>
<h1>Move Node</h1>

<p>The move node methods are also quite straightforward,
they require the lft of the node to be moved and the lft of the node to be moved to.<br />
To move a node you can use one of the following methods:</p>

<ul>
<li>
<b>move_node_before(<var>lft</var>, <var>lft of other node</var>)</b>
<p>moves the first node before the other node</p> </li>
<li>
<b>move_node_after(<var>lft</var>, <var>lft of other node</var>)</b>
<p>moves the first node after the other node</p> </li>

<li>
<b>move_node_append(<var>lft</var>, <var>lft of other node</var>)</b>
<p>moves the first node to the position of first child to the other node</p> </li>
<li>
<b>move_node_append_last(<var>lft</var>, <var>lft of other node</var>)</b>
<p>moves first the node to the position of last child to the other node</p> </li>
<li>
<b>move_node(<var>source lft</var>, <var>target lft</var>)</b>
<p>raw access, uses the source and target lfts directly <b>!Discouraged!</b></p> </li>

</ul>
<p>If the operation was successful, the return value is an array with the new <kbd>lft</kbd> and <kbd>rgt</kbd> value of the moved node(s),<br />
otherwise false is returned.</p>

<p>For example:</p>

<code>$this-&gt;MPTtree-&gt;move_node_before(4,2);
// Moves A new child! to the position of first child of Root Page
</code>

<a name="delete">&nbsp;</a>
<h1>Deleting Nodes</h1>

<p>To delete a node you have two options:</p>

<ul>
<li>Delete a node and promote all subnodes, or </li>
<li>Delete a whole subtree (node and all descendants)</li>
</ul>

<p>The data required to perform a delete is only the lft value of the node to delete.</p>

<p>Using the table above (from Insert Node):</p> 

<code>
$this-&gt;MPTtree-&gt;delete_node(3); // deletes the node A child, promoting A new child!<br />
// or:<br />
$this-&gt;MPTtree-&gt;delete_branch(3); // deletes the node A child and the descendants of it (A new Child!)
</code>

<a name="validate">&nbsp;</a>
<h1>Validate Tree/Node</h1>

<p>To validate the tree you can use one of the following methods:</p>

<h2>is_valid_node(<var>lft</var>)</h2>

<p>Returns true if the node at the lft supplied is a valid node</p>

<h2>validate([<var>return text</var>])</h2>

<p>Validates the tree, searches for gaps and duplicates
(because they are the primary indicators of errors in the tree).</p>

<p>If <var>return text</var> is set to <kbd>true</kbd> (default), a string with all encountered errors will be returned.
If <var>return text</var> is set to <kbd>false</kbd>, <kbd>true</kbd> as return will indicate a valid tree and
<kbd>false</kbd> will indicate that it is an error somewhere</p>

<h2>display(<var>lft</var>)</h2>

<p>Prints a simple tree in HTML with the tree structure, starting with the node with the lft <var>lft</var></p>


<a name="ORM">&nbsp;</a>
<h1>Object-Relational Mapping (ORM)</h1>

<p>
	An easier way to fetch nodes and to traverse the tree is to let objects represent the nodes in the tree.
	In this way you can move and insert nodes easier (some agrees, some does not).<br />
	Currently there is no support for relationships between tables (ie. OneToMany etc.).
</p>

<p>
	The <kbd>MPTtree_ORM.php</kbd> is usually stored inside the folder <dfn>MPTtree</dfn>, which lies in the same directory as <kbd>mpttree.php</kbd>. If it is stored elewhere, please change the <kbd>$inc_dir</kbd> variable in <kbd>mpttree.php</kbd>.<br />
	(The same applies to the <kbd>MPTTree_iterator.php</kbd> file)
</p>


<h2>Fetch node, using get_ORM([<var>lft</var>])</h2>

<p>To fetch an ORM node from MPTtree, use the <dfn>get_ORM()</dfn> method:</p>

<code>
	$root = $this-&gt;MPTtree-&gt;get_ORM();<br />
	$other_node = $this-&gt;MPTtree-&gt;get_ORM(4);<br />
	$xpath_node = $this-&gt;MPTtree-&gt;get_ORM('/apple/green'); // fetching node with a path (arrays are also accepted)
</code>


<h2>Fetch children and descendants</h2>

<p>To fetch children or descendants from a node, you use the <dfn>children()</dfn> and <dfn>descendants()</dfn> methods of the ORM objects.
They return an array with the children/descendants as ORM objects.</p>

<code>
// loop through all children<br />
foreach($node-&gt;children() as $child){<br />
&nbsp;&nbsp;&nbsp;&nbsp;// print their name<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $child-&gt;get('name');<br />
}
</code>

<p>There are also two methods named <dfn>count_children()</dfn> and <dfn>count_descendants</dfn>, which returns the count of the descendants / children of a given node.<br />
To check if a node has children use the <dfn>has_children()</dfn> method.
</p>


<h2>Fetch parent or root node</h2>

<p>To fetch the parent of a node, use <dfn>parent()</dfn>, to fetch the root node use <dfn>root()</dfn>.</p>

<code>
	$parent = $node-&gt;parent();<br />
	$root = $node-&gt;root();
</code>

<p>To see if a node is the root node of a tree, use the <dfn>is_root()</dfn>.</p>

<p>If there are no parent (ie. the node is a root node or an orphan), <dfn>parent()</dfn> returns <kbd>false</kbd>.<br />
	The same applies for <dfn>root()</dfn> when the node is an orphan.</p>


<h2>Fetch / set values</h2>

<p>To fetch a value from a node, use the <dfn>get(</dfn>'<var>column name</var>'<dfn>)</dfn> method.<br />
	To set a value on a node, use the <dfn>set(</dfn>'<var>column name</var>'<dfn>, </dfn>value<dfn>)</dfn>.</p>

<p>If you then want to <strong>make the changes</strong> to the database, call the <dfn>update()</dfn> method.<br />
	To refresh the data of a node, use the <dfn>refresh()</dfn> method (Note: using refersh can result in the node becoming an orphan, if it has been deleted).
</p>

<p><strong>Note:</strong>&nbsp; The values you get / set with <dfn>get()</dfn> / <dfn>set()</dfn> is not auto serialized/unserialized, thus you have to do that yourself if required.</p>


<h2>Path and xpath()</h2>

<p>To fetch a node, you can traverse the tree normally using <dfn>children()</dfn> and then selecting nodes matching segments in a path. This is very inefficient if you only are after the node (and not the attributes of the parents of a node), but there is a method which can help: <dfn>xpath()</dfn>.</p>

<p>The <dfn>xpath()</dfn> method works almost exactly like the MPTtree method <a href="#get_xpath">xpath()</a>. The difference is that the ORM variant uses relative paths as default, where the original uses absolute as default:</p>

<code>
	$fetched_node = $node-&gt;xpath('/apples/red'); // relative<br />
	$fetched_node = $node-&gt;xpath('&amp;fruits&amp;apples&amp;red', '&amp;', false); // not relative (note: here we are using &amp; as a path separator)
</code>

<p>To get the path of a node, use the <dfn>path()</dfn> method, it returns the path of a node in an array. As deault it excludes the root node in the array (thanks to that we can use the path directly in <dfn>xpath</dfn>), if the first parameter is <kbd>true</kbd> a path with the root node is generated.
</p>

<code>
	$fetched_node = $node-&gt;xpath($other_node-&gt;path(), null, false); // rather meaningless, but it's an example
</code>


<h2>Insert node</h2>

<p>To insert a node, use one of the following methods:</p>

<ul>
	<li><b>insert_as_root()</b></li>
	<li><b>insert_above(<var>node</var>)</b></li>
	<li><b>insert_below(<var>node</var>)</b></li>
	<li><b>insert_as_first_child_of(<var>node</var>)</b></li>
	<li><b>insert_as_last_child_of(<var>node</var>)</b></li>
</ul>

<p><strong>Note:</strong>&nbsp;
The node to be inserted needs to be an orphan (It cannot belong to a tree before insert).</p>


<h2>Move node</h2>

<p>To move a node, use one of the following methods:</p>

<ul>
	<li><b>move_above(<var>node</var>)</b></li>
	<li><b>move_below(<var>node</var>)</b></li>
	<li><b>move_to_first_child_of(<var>node</var>)</b></li>
	<li><b>move_to_last_child_of(<var>node</var>)</b></li>
</ul>

<p>Example:</p>

<code>
$node1-&gt;move_above($node2); // will put $node1 above $node2 in the tree, they share the same parent but node1 has a lower lft<br />
<br />
$node1-&gt;move_node_to_fisrt_child_of($node2); // positions $node1 as the first child of $node2
</code>


<h2>Create new node</h2>

<p>To create a new orm object, use the <dfn>new_ORM()</dfn> method:</p>

<code>
	$new = $this-&gt;MPTtree-&gt;new_ORM();<br />
	$new-&gt;set('title', 'New Node');<br />
	$new-&gt;insert_as_first_child_of($old);
</code>

<p>The orm objects also have a <dfn>new_ORM()</dfn> method themselves which performs the exact same thing.</p>

<p>The new node created is an orphan, it doesn't belong to a tree.</p>


<h2>Delete node</h2>

<p>To delete a node from the tree, use the <dfn>delete()</dfn> method:</p>

<code>
	$node-&gt;delete(); // deletes the node from the tree, promoting children<br />
	$node-&gt;delete(true); // deletes the node plus all descendants of it, the data of the descendants are lost
</code>

<p>The deleted node loses all connections with other nodes in the tree, and it's lft, rgt and id, becoming an orphan.
	It does not however lose it's data, and you can insert the node back into the tree, or continue to fetch data from it.</p>
	
<p class="important"><strong>Important:</strong>&nbsp; To check if a node is an orphan, use the <dfn>is_orphan()</dfn> method on the node, it returns <kbd>true</kbd> if the node is an orphan.</p> 


<a name="iterators">&nbsp;</a>
<h1>Iterators</h1>

<p class="important"><strong>Important:</strong>&nbsp;
Iterators are <strong>only</strong> supported in PHP 5 or greater!<br />
This because they utilize the Iterator interface, which PHP 4 does not support.</p>

<p>I have implemented iterators to make it easier to iterate over the tree,
to use them, fetch an iterator from the MPTtree instance with the help of the <dfn>iterator()</dfn> method.</p>

<p>Example:</p>

<code>
$this-&gt;load-&gt;MPTT('page_tree', 'pages');<br />
<br />
foreach($this-&gt;pages-&gt;iterator() as $node){<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $node['title'];<br />
}
</code>

<p>There is also an ORM variant of the itetator: <dfn>ORMiterator()</dfn></p>

<code>
$this-&gt;load-&gt;MPTT('page_tree', 'pages');<br />
<br />
foreach($this-&gt;pages-&gt;ORMiterator() as $node){<br />
&nbsp;&nbsp;&nbsp;&nbsp;echo $node-&gt;path();<br />
}
</code>

<p>If you use the ORM objects, you can iterate over the descendants of a node with the node method iterator()</p>

<a name="change">&nbsp;</a>
<h1>Change Log</h1>

<h2>Version 0.1.6-fix</h2>

<p>Release Date: March 27, 2008</p>

<ul>
	<li>Fixed error with call to <dfn>db::get_where()</dfn> in <dfn>move_node()</dfn></li>
	<li>Corrected return values from <dfn>move_node()</dfn>, now it returns a normal array, just like <dfn>insert_node()</dfn></li>
	<li><dfn>tree2array()</dfn> now returns false if the root node cannot be found</li>
	<li>Added some debug messages</li>
	<li>Renamed <kbd>MPTtree.php</kbd> to <kbd>mpttree.php</kbd> to avoid include errors under linux</li>
	<li>Minor corrections in the manual</li>
</ul>

<h2>Version 0.1.6</h2>

<p>Release Date: March 22, 2008</p>

<ul>
<li>Added <dfn>tree2array()</dfn></li>
<li>Added <dfn>get_descendants_wlevel()</dfn></li>
<li>Added <dfn>has_children()</dfn></li>
<li>Added <dfn>has_children()</dfn> to ORM object</li>
<li>Added command swith to <dfn>get_descendants()</dfn> to make it call <dfn>get_descendants_wlevel()</dfn> instead, thus returning a level column</li>
<li><dfn>get_ORM()</dfn> can now also accept a path (string or array doesn't matter) to the node which to load.</li>
</ul>

<h2>Version 0.1.5</h2>

<p>Release Date: March 13, 2008</p>

<ul>
<li>Introducing an ORM wrapper for MPTtree</li>
<li>Added a MPTtreeIterator, to iterate over descendants in the tree</li>
<li>Added a MPTtreeORMIterator, to iterate over descendants in the tree, returning ORM objects</li>
<li>Added <dfn>get_node_byid()</dfn></li>
<li>Added an (optional) custom MY_Loader.php, to make it easier to have multiple instances at the same time (no need extending classes).</li>
<li>Improved <dfn>get_parent()</dfn></li>
<li>Improved <dfn>count_children()</dfn>, not as much database traffic and counting is done in database</li>
<li>Improved <dfn>xpath()</dfn>, more accurate (doesn't need <dfn>xpath2()</dfn> anymore)</li>
<li>Changed the return values from the insert and move methods to return the new lft and rgt values instead of true.</li>
<li><dfn>xpath()</dfn> and <dfn>xpath2()</dfn> can now also accept an array as path (no need using separators)</li>
</ul>

<h2>Version 0.1.4</h2>

<p>Release Date: February 26, 2008</p>

<ul>
<li>Added a few methods to be used with Active Record, see <a href="#active">Active Record helpers</a></li>
<li>Added calls to <dfn>db::protect_identifiers()</dfn> in <dfn>set_opts()</dfn>
and in <dfn>set_table()</dfn></li>
<li>Replaced the method <dfn>xpath()</dfn> with a much more efficient xpath variant.</li>
<li>The old <dfn>xpath()</dfn> method have been renamed to <dfn>xpath2()</dfn>.</li>
<li>HCG: Replaced use of transactions with locks</li>
</ul>

<h2>Version 0.1.2</h2>

<p>Release Date: February 20, 2008</p>

<ul>
<li>Added the method <dfn>xpath()</dfn></li>
<li>Remade <dfn>set_opts()</dfn> to accept an array with the parameters</li>
<li>Added a sanitation of the data to <dfn>update_node()</dfn> and to the <a href="#insert">insert methods</a>.
This to prevent damage to the tree.</li>
</ul>

<h2>Version 0.1.1</h2>

<p>Release Date: February 18, 2008</p>

<ul>
<li>Added transaction support</li>
<li>Added the method <dfn>get_descendants_where()</dfn></li>
<li>Added the method <dfn>get_children_where()</dfn></li>
<li>Changed algohrithm for <dfn>get_children()</dfn>, the new is almost twice as fast as the old one!</li>
<li>Renamed some internal properties to address some conflicts with different CI libraries</li>
<li>Changed name of <dfn>get_decendants()</dfn> to <dfn>get_descendants()</dfn>,
a bad misspelling by me.</li>
</ul>

<h2>Version 0.1.0</h2>

<p>Release Date: February 9, 2008</p>

<ul>
<li>Initial Release</li>
</ul>

</div>
<!-- END CONTENT -->


<div id="footer">
<p>
<a href="#top">Top of Page</a>&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;
<a href="#">User Guide Home</a>
</p>
<p>Copyright &copy; 2008, Martin Wernstahl &lt;m4rw3r at gmail dot com&gt;</p>
<p><strong>Thanks To:</strong><br />
Hamish Guthrie &lt;hamish at prodigi dot ch&gt; (Table locking)</p>
<p>MPTtree is released under <a href="http://www.gnu.org/licenses/lgpl.html" title="GNU Lesser General Public License">LGPL</a></p>

<p><a href="http://codeigniter.com">CodeIgniter</a> &nbsp;&middot;&nbsp; Copyright &#169; 2007 &nbsp;&middot;&nbsp; <a href="http://ellislab.com/">Ellislab, Inc.</a></p>
</div>

</body>
</html>